/*
 * customer.c
 *
 *  Created on: Apr 22, 2011
 *      Author: Gilad & Yotam
 */

#include "customer.h"
#include "bill.h"
#include "commontools.h"
#include <assert.h>

struct Customer_t {
	char* name;
	int id;
	List rentedCars; //a list which elements are of Car type
	List bill; // a list which elements are of Bill type
	int totalFee;
	int totalPayed;
};

void* copyCustomer(void* element) {
	if (element == NULL) {
		return NULL;
	}
	Customer customer = (Customer) element;
	Customer newCustomer = malloc(sizeof(struct Customer_t));
	if (newCustomer == NULL) {
		return NULL;
	}

	/*making sure no illegal operations will take place if destroying the
	 * newCustomer before the end of this function:
	 */
	newCustomer->rentedCars = NULL;
	newCustomer->bill = NULL;

	newCustomer->name = copyName(customer->name);
	if (newCustomer->name == NULL) {
		freeCustomer(newCustomer);
		return NULL;
	}
	newCustomer->rentedCars = listCopy(customer->rentedCars);
	if (newCustomer->rentedCars == NULL) {
		freeCustomer(newCustomer);
		return NULL;
	}
	newCustomer->bill = listCopy(customer->bill);
	if (newCustomer->bill == NULL) {
		freeCustomer(newCustomer);
		return NULL;
	}
	newCustomer->id = customer->id;
	newCustomer->totalFee = customer->totalFee;
	newCustomer->totalPayed = customer->totalPayed;
	return newCustomer;
}

void freeCustomer(void* element) {
	if (element == NULL) {
		return;
	}
	Customer customer = (Customer) element;
	free(customer->name);
	listDestroy(customer->rentedCars);
	listDestroy(customer->bill);
	free(customer);
}

int compareCustomerId(void* element1, void* element2) {
	return (((Customer) element1)->id - ((Customer) element2)->id);
}

int compareCustomerFee(void* element1, void* element2) {
	return (((Customer) element2)->totalFee - ((Customer) element1)->totalFee);
}

bool customerFilterOutNoFee(void* element) {
	return (((Customer) element)->totalFee > 0);
}

Customer customerCreate(int id, char* name) {
	if (name == NULL) {
		return NULL;
	}
	Customer newCustomer = malloc(sizeof(struct Customer_t));
	if (newCustomer == NULL) {
		return NULL;
	}
	newCustomer->rentedCars = NULL;
	newCustomer->bill = NULL;
	newCustomer->name = copyName(name);
	if (name == NULL) {
		freeCustomer(newCustomer);
		return NULL;
	}
	newCustomer->rentedCars = listCreate(carCopy, carFree);
	if (newCustomer->rentedCars == NULL) {
		freeCustomer(newCustomer);
		return NULL;
	}
	newCustomer->bill = listCreate(copyBillElement, freeBillElement);
	if (newCustomer->bill == NULL) {
		freeCustomer(newCustomer);
		return NULL;
	}
	newCustomer->id = id;
	newCustomer->totalFee = 0;
	newCustomer->totalPayed = 0;
	return newCustomer;
}

CustomerResult customerRentCar(Customer customer, Car car) {
	if ((customer == NULL) || (car == NULL)) {
		return CUSTOMER_NULL_ARGUMENT;
	}
	ListResult result = listInsertLast(customer->rentedCars, car);
	if (result == LIST_OUT_OF_MEMORY) {
		return CUSTOMER_OUT_OF_MEMORY;
	}
	assert(result == LIST_SUCCESS);
	return CUSTOMER_SUCCESS;
}

CustomerResult customerReturnCar(Customer customer, int license, int km) {
	if (customer == NULL) {
		return CUSTOMER_NULL_ARGUMENT;
	}
	LIST_FOREACH(Car, currentCar, customer->rentedCars) {
		if (carGetLicensePlate(currentCar) == license) {
			/**
			 * The car was found in the list. move it to the bill and
			 * remove it from the rented cars list.
			 */
			BillElement newBill = billCreateNewElement(currentCar, km);
			if (newBill == NULL) {
				return CUSTOMER_OUT_OF_MEMORY;
			}
			ListResult listResult = listInsertLast(customer->bill, newBill);
			if (listResult == LIST_OUT_OF_MEMORY) {
				freeBillElement(newBill);
				return CUSTOMER_OUT_OF_MEMORY;
			}
			/*update the total fee for the customer*/
			customer->totalFee += billGetPrice(newBill);

			freeBillElement(newBill);
			/**
			 * the list internal iterator is pointing to the element
			 * we would like to remove. so remove it, and finish.
			 */
			listRemoveCurrent(customer->rentedCars);
			return CUSTOMER_SUCCESS;
		}
	}
	/* if got here, it means the car is not in the list. */
	return CUSTOMER_CAR_NOT_RENTED;
}

char* customerGetName(Customer customer) {
	if (customer == NULL) {
		return NULL;
	}
	return customer->name;
}

int customerGetId(Customer customer) {
	return customer->id;
}

int customerGetFee(Customer customer) {
	return customer->totalFee;
}

int customerGetPastPayment(Customer customer) {
	return customer->totalPayed;
}

List customerGetBillList(Customer customer) {
	if (customer) {
		List newListCopy = listCopy(customer->bill);
		/**
		 * returns NULL if memory allocation failed or the
		 * requested list if succeeded
		 */
		return newListCopy;
	}
	return NULL;
}

List customerGetRentedCarsList(Customer customer) {
	if (customer) {
		List newListCopy = listCopy(customer->rentedCars);
		/**
		 * returns NULL if memory allocation failed or the
		 * requested list if succeeded
		 */
		return newListCopy;
	}
	return NULL;
}

int customerGetNumberOfRentedCars(Customer customer) {
	return listGetSize(customer->rentedCars);
}

int customerGetNumberOfCarsInBill(Customer customer) {
	return listGetSize(customer->bill);
}

CustomerResult customerPayBill(Customer customer) {
	if (customer == NULL) {
		return CUSTOMER_NULL_ARGUMENT;
	}
	customer->totalPayed += customer->totalFee;
	customer->totalFee = 0;
	listClear(customer->bill);
	return CUSTOMER_SUCCESS;
}
